
#include <material/io/iodefines.h>
#include <material/io/materialreader.h>
#include <material/materialmanager.h>
#include <session.h>
#include <qfile.h>


cceMaterialReader::cceMaterialReader(cceMaterialManager *manager)
  : _manager (manager)
{
}

void cceMaterialReader::ReadAll()
{
  _manager->Clear();

  ReadTextures();
	ReadShaders();
	ReadPrograms();
	ReadEffects();
	ReadMaterials();
}


/* ************************
 *   Reading textures
 * ************************/

void cceMaterialReader::ReadTextures()
{
  QString filename = cceSession::Get()->GetFileName(CCE_MATERIAL_IO_TEXTURES_FILENAME);
  QFile file (filename);
  if (!file.open(QIODevice::ReadOnly))
    {
      return;
    }

  QDomDocument doc;
  if (!doc.setContent(&file))
    {
      file.close();
      return;
    }

  file.close();

  ReadTextures(doc.documentElement());
}

void cceMaterialReader::ReadTextures(QDomElement element)
{
  if (element.tagName() != CCE_MATERIAL_IO_XML_TEXTURES)
    {
      return;
    }

	for (QDomElement textureElement = element.firstChildElement(CCE_MATERIAL_IO_XML_TEXTURE);
       !textureElement.isNull();
			 textureElement = textureElement.nextSiblingElement(CCE_MATERIAL_IO_XML_TEXTURE))
    {
      ReadTexture(textureElement);
    }
}

void cceMaterialReader::ReadTexture(QDomElement element)
{
  if (element.tagName() != CCE_MATERIAL_IO_XML_TEXTURE)
    {
      return;
    }

  QString name = element.attribute(CCE_MATERIAL_IO_XML_TEXTURE_NAME);
  if (name.isNull() || name.length() == 0)
    {
      return;
    }

  QDomElement filenameElement = element.firstChildElement(CCE_MATERIAL_IO_XML_TEXTURE_FILENAME);
  if (filenameElement.isNull())
    {
      return;
    }
  QString filename = filenameElement.text();
  if (filename.isNull() || filename.length() == 0)
    {
      return;
    }

  QDomElement groupElement = element.firstChildElement(CCE_MATERIAL_IO_XML_TEXTURE_GROUP);
  QString group = "";
  if (!groupElement.isNull())
    {
      group = groupElement.text();
    }

  QDomElement wrapElement = element.firstChildElement(CCE_MATERIAL_IO_XML_TEXTURE_WRAP);
  if (wrapElement.isNull())
    {
      return;
    }

  QString wrapText = wrapElement.text();
  if (wrapText.isNull() || wrapText.length() == 0)
    {
      return;
    }

  cceMaterialTexture::Wrap wrap = cceMaterialTexture::GetWrap(wrapText);


  cceMaterialTexture* texture = new cceMaterialTexture ();
  texture->SetNew(false);
  texture->SetName(name);
  texture->SetFilename(filename);
  texture->SetGroup(group);
  texture->SetWrap(wrap);

  _manager->AddTexture(texture);

}



/* ************************
 *   Reading shaders
 * ************************/

void cceMaterialReader::ReadShaders()
{
	QString filename = cceSession::Get()->GetFileName(CCE_MATERIAL_IO_SHADERS_FILENAME);
  QFile file (filename);
  if (!file.open(QIODevice::ReadOnly))
    {
      return;
    }

  QDomDocument doc;
  if (!doc.setContent(&file))
    {
      file.close();
      return;
    }

  file.close();

	ReadShaders(doc.documentElement());
}

void cceMaterialReader::ReadShaders(QDomElement element)
{
	if (element.tagName() != CCE_MATERIAL_IO_XML_SHADERS)
    {
      return;
    }

	for (QDomElement shaderElement = element.firstChildElement(CCE_MATERIAL_IO_XML_SHADER);
			 !shaderElement.isNull();
			 shaderElement = shaderElement.nextSiblingElement(CCE_MATERIAL_IO_XML_SHADER))
    {
			ReadShader(shaderElement);
    }
}

void cceMaterialReader::ReadShader(QDomElement element)
{
	if (element.tagName() != CCE_MATERIAL_IO_XML_SHADER)
    {
      return;
    }

	QString name = element.attribute(CCE_MATERIAL_IO_XML_SHADER_NAME);
  if (name.isNull() || name.length() == 0)
    {
      return;
    }
	QString typeText = element.attribute(CCE_MATERIAL_IO_XML_SHADER_TYPE);
	if (typeText.isNull() || typeText.length() == 0)
    {
      return;
    }
	cceMaterialShader::Type type = cceMaterialShader::GetType(typeText);

	QString codeText = element.text();

	cceMaterialShader* shader = new cceMaterialShader ();
	shader->SetNew(false);
	shader->SetName(name);
	shader->SetType(type);
	shader->SetCode(codeText);
	shader->UpdateBindings();

	_manager->AddShader(shader);

}




/* ************************
 *   Reading programs
 * ************************/

void cceMaterialReader::ReadPrograms()
{
	QString filename = cceSession::Get()->GetFileName(CCE_MATERIAL_IO_PROGRAMS_FILENAME);
	QFile file (filename);
	if (!file.open(QIODevice::ReadOnly))
		{
			return;
		}

	QDomDocument doc;
	if (!doc.setContent(&file))
		{
			file.close();
			return;
		}

	file.close();

	ReadPrograms(doc.documentElement());
}

void cceMaterialReader::ReadPrograms(QDomElement element)
{
	if (element.tagName() != CCE_MATERIAL_IO_XML_PROGRAMS)
		{
			return;
		}

	for (QDomElement programElement = element.firstChildElement(CCE_MATERIAL_IO_XML_PROGRAM);
			 !programElement.isNull();
			 programElement = programElement.nextSiblingElement(CCE_MATERIAL_IO_XML_PROGRAM))
		{
			ReadProgram(programElement);
		}
}


void cceMaterialReader::ReadProgram(QDomElement element)
{
	if (element.tagName() != CCE_MATERIAL_IO_XML_PROGRAM)
		{
			return;
		}

	QString name = element.attribute (CCE_MATERIAL_IO_XML_PROGRAM_NAME);
	if (name.isNull() || name.length() == 0)
		{
			return;
		}

	QDomElement techniquesElement = element.firstChildElement(CCE_MATERIAL_IO_XML_PROGRAM_TECHNIQUES);
	if (techniquesElement.isNull())
		{
			return;
		}

	cceMaterialProgram* program = new cceMaterialProgram ();
	program->SetNew(false);
	program->SetName(name);

	for (QDomElement techniqueElement = techniquesElement.firstChildElement(CCE_MATERIAL_IO_XML_PROGRAM_TECHNIQUE);
			 !techniqueElement.isNull();
			 techniqueElement = techniqueElement.nextSiblingElement(CCE_MATERIAL_IO_XML_PROGRAM_TECHNIQUE))
		{
			cceMaterialProgramTechnique* technique = new cceMaterialProgramTechnique ();

			for (QDomElement shaderElement = techniqueElement.firstChildElement(CCE_MATERIAL_IO_XML_PROGRAM_SHADER);
					 !shaderElement.isNull();
					 shaderElement = shaderElement.nextSiblingElement(CCE_MATERIAL_IO_XML_PROGRAM_SHADER))
				{
					QString name = shaderElement.text();
					cceMaterialShader* shader = _manager->GetShader(name);
					if (shader)
						{
							technique->AddShader(shader);
						}
				}
			program->AddTechnique(technique);
		}

	_manager->AddProgram(program);
}



/* ************************
 *   Reading effects
 * ************************/

void cceMaterialReader::ReadEffects()
{
	QString filename = cceSession::Get()->GetFileName(CCE_MATERIAL_IO_EFFECTS_FILENAME);
	QFile file (filename);
	if (!file.open(QIODevice::ReadOnly))
		{
			return;
		}

	QDomDocument doc;
	if (!doc.setContent(&file))
		{
			file.close();
			return;
		}

	file.close();

	ReadEffects(doc.documentElement());
}

void cceMaterialReader::ReadEffects(QDomElement element)
{
	if (element.tagName() != CCE_MATERIAL_IO_XML_EFFECTS)
		{
			return;
		}

	for (QDomElement programElement = element.firstChildElement(CCE_MATERIAL_IO_XML_EFFECT);
			 !programElement.isNull();
			 programElement = programElement.nextSiblingElement(CCE_MATERIAL_IO_XML_EFFECT))
		{
			ReadEffect(programElement);
		}
}


void cceMaterialReader::ReadEffect(QDomElement element)
{
	if (element.tagName() != CCE_MATERIAL_IO_XML_EFFECT)
		{
			return;
		}

	QString name = element.attribute (CCE_MATERIAL_IO_XML_EFFECT_NAME);
	if (name.isNull() || name.length() == 0)
		{
			return;
		}

	QDomElement stagesElement = element.firstChildElement(CCE_MATERIAL_IO_XML_EFFECT_STAGES);
	if (stagesElement.isNull())
		{
			return;
		}

	cceMaterialEffect* effect = new cceMaterialEffect();
	effect->SetNew(false);
	effect->SetName(name);

	for (QDomElement stageElement = stagesElement.firstChildElement(CCE_MATERIAL_IO_XML_EFFECT_STAGE);
			 !stageElement.isNull();
			 stageElement = stageElement.nextSiblingElement(CCE_MATERIAL_IO_XML_EFFECT_STAGE))
		{
			cceMaterialEffectStage* stage = new cceMaterialEffectStage();

			for (QDomElement passElement = stageElement.firstChildElement(CCE_MATERIAL_IO_XML_EFFECT_PASS);
					 !passElement.isNull();
					 passElement = passElement.nextSiblingElement(CCE_MATERIAL_IO_XML_EFFECT_PASS))
				{
					QString passText = passElement.attribute(CCE_MATERIAL_IO_XML_EFFECT_NAME);
					if (passText.isNull() || passText.length() == 0)
						{
							continue;
						}
					cceMaterialPass pass = cceMaterialEffectStage::GetPass(passText);

					QString programName = passElement.text();
					cceMaterialProgram* program = _manager->GetProgram(programName);
					if (program)
						{
							stage->SetPass(pass, program);
						}
				}
			effect->AddStage(stage);
		}

	_manager->AddEffect(effect);
}



/* ************************
 *   Reading materials
 * ************************/

void cceMaterialReader::ReadMaterials()
{
	QString filename = cceSession::Get()->GetFileName(CCE_MATERIAL_IO_MATERIALS_FILENAME);
	QFile file (filename);
	if (!file.open(QIODevice::ReadOnly))
		{
			return;
		}

	QDomDocument doc;
	if (!doc.setContent(&file))
		{
			file.close();
			return;
		}

	file.close();

	ReadMaterials(doc.documentElement());
}

void cceMaterialReader::ReadMaterials(QDomElement element)
{
	if (element.tagName() != CCE_MATERIAL_IO_XML_MATERIALS)
		{
			return;
		}

	for (QDomElement materialElement = element.firstChildElement(CCE_MATERIAL_IO_XML_MATERIAL);
			 !materialElement.isNull();
			 materialElement = materialElement.nextSiblingElement(CCE_MATERIAL_IO_XML_MATERIAL))
		{
			ReadMaterial(materialElement);
		}
}

namespace
{
QColor read_color (QDomElement element)
{
	QColor c;
	bool ok;
	if (element.hasAttribute("r"))
		{
			unsigned r = element.attribute("r").toInt(&ok);
			if (ok) c.setRed(r);
		}
	if (element.hasAttribute("g"))
		{
			unsigned g = element.attribute("g").toInt(&ok);
			if (ok) c.setGreen(g);
		}
	if (element.hasAttribute("b"))
		{
			unsigned b = element.attribute("b").toInt(&ok);
			if (ok) c.setBlue(b);
		}
	if (element.hasAttribute("a"))
		{
			unsigned a = element.attribute("a").toInt(&ok);
			if (ok) c.setAlpha(a);
		}
	return c;
}
}

void cceMaterialReader::ReadMaterial(QDomElement element)
{
	if (element.tagName() != CCE_MATERIAL_IO_XML_MATERIAL)
		{
			return;
		}

	QString name = element.attribute(CCE_MATERIAL_IO_XML_MATERIAL_NAME);
	if (name.isNull() || name.length() == 0)
		{
			return;
		}

	cceMaterialMaterial* material = new cceMaterialMaterial ();
	material->SetNew(false);
	material->SetName(name);

	QDomElement ambientElement = element.firstChildElement(CCE_MATERIAL_IO_XML_MATERIAL_AMBIENT);
	if (!ambientElement.isNull())
		{
			material->SetAmbient(read_color(ambientElement));
		}
	QDomElement diffuseElement = element.firstChildElement(CCE_MATERIAL_IO_XML_MATERIAL_DIFFUSE);
	if (!diffuseElement.isNull())
		{
			material->SetDiffuse(read_color(diffuseElement));
		}
	QDomElement specularElement = element.firstChildElement(CCE_MATERIAL_IO_XML_MATERIAL_SPECULAR);
	if (!specularElement.isNull())
		{
			material->SetSpecular(read_color(specularElement));
		}
	QDomElement shininessElement = element.firstChildElement(CCE_MATERIAL_IO_XML_MATERIAL_SHININESS);
	if (!shininessElement.isNull())
		{
			QString valueText = shininessElement.attribute(CCE_MATERIAL_IO_XML_MATERIAL_VALUE);
			bool ok;
			float v = valueText.toFloat(&ok);
			if (ok) material->SetShininess(v);
		}
	QDomElement emissionElement = element.firstChildElement(CCE_MATERIAL_IO_XML_MATERIAL_EMISSION);
	if (!emissionElement.isNull())
		{
			material->SetEmission(read_color(emissionElement));
		}

	QDomElement effectElement = element.firstChildElement(CCE_MATERIAL_IO_XML_MATERIAL_EFFECT);
	if (!effectElement.isNull())
		{
			QString effectName = effectElement.text();
			if (!effectName.isNull() && effectName.length() > 0)
				{
					cceMaterialEffect* effect = _manager->GetEffect(effectName);
					if (effect)
						{
							material->SetEffect(effect);
						}
				}
		}

	QDomElement texturesElement = element.firstChildElement(CCE_MATERIAL_IO_XML_MATERIAL_TEXTURES);
	if (!texturesElement.isNull())
		{
			for (QDomElement passElement = texturesElement.firstChildElement(CCE_MATERIAL_IO_XML_MATERIAL_PASS);
					 !passElement.isNull();
					 passElement = passElement.nextSiblingElement(CCE_MATERIAL_IO_XML_MATERIAL_PASS))
				{
					QString passText = passElement.attribute(CCE_MATERIAL_IO_XML_MATERIAL_NAME);
					if (passText.isNull() || passText.length() == 0)
						{
							continue;
						}
					cceMaterialPass pass = cceMaterialEffectStage::GetPass(passText);


					for (QDomElement textureElement = passElement.firstChildElement(CCE_MATERIAL_IO_XML_MATERIAL_TEXTURE);
							 !textureElement.isNull();
							 textureElement = textureElement.nextSiblingElement(CCE_MATERIAL_IO_XML_MATERIAL_TEXTURE))
						{
							QString unitText = textureElement.attribute(CCE_MATERIAL_IO_XML_MATERIAL_UNIT);
							bool ok;
							unsigned unit = unitText.toUInt(&ok);
							if (unit >= 16)
								{
									continue;
								}

							QString textureName = textureElement.text();
							if (textureName.isNull() || textureName.length() == 0)
								{
									continue;
								}

							cceMaterialTexture* texture = _manager->GetTexture(textureName);
							if (!texture)
								{
									continue;
								}

							material->SetTexture(pass, unit, texture);
						}
				}
		}

	_manager->AddMaterial(material);
}
